package com.dd.cloud.user.service.doctor.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dd.cloud.common.constants.UserConstants;
import com.dd.cloud.user.entity.doctor.PatientDeliveryAddr;
import com.dd.cloud.user.entity.doctor.PatientMedCard;
import com.dd.cloud.user.entity.patient.PatientAddressLink;
import com.dd.cloud.user.entity.patient.PatientUserAddress;
import com.dd.cloud.user.mapper.doctor.PatientDeliveryAddrMapper;
import com.dd.cloud.user.req.doctor.PatientDeliveryAddrCreateReq;
import com.dd.cloud.user.req.doctor.PatientDeliveryAddrH5CreateReq;
import com.dd.cloud.user.req.doctor.PatientDeliveryAddrUpdateReq;
import com.dd.cloud.user.service.doctor.IPatientDeliveryAddrService;
import com.dd.cloud.user.service.doctor.IPatientMedCardService;
import com.dd.cloud.common.enums.ZkExceptionEnum;
import com.dd.cloud.common.exception.ZekeException;
import com.dd.cloud.common.utils.ObjectUtils;
import com.dd.cloud.user.service.patient.IPatientAddressLinkService;
import com.dd.cloud.user.service.patient.IPatientUserAddressService;
import org.apache.catalina.User;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <p>
 * 患者配送地址表 服务实现类
 * </p>
 *
 * @author czg
 * @since 2022-09-28
 */
@Service
public class PatientDeliveryAddrServiceImpl extends ServiceImpl<PatientDeliveryAddrMapper, PatientDeliveryAddr> implements IPatientDeliveryAddrService {
    @Autowired
    private IPatientMedCardService patientMedCardService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private IPatientAddressLinkService patientAddressLinkService;

    @Autowired
    private IPatientUserAddressService patientUserAddressService;

    /**
     * 查询当前患者的配送地址列表
     *
     * @param medId
     * @return
     */
    @Override
    public List<PatientDeliveryAddr> getPatientDeliveryAddrList(String medId) {
        QueryWrapper<PatientDeliveryAddr> qw = new QueryWrapper<>();
        qw.lambda().eq(PatientDeliveryAddr::getMedId, medId)
                .eq(PatientDeliveryAddr::getStatus, UserConstants.DeliveryAddrStatus.NORMAL)
                .orderByDesc(PatientDeliveryAddr::getCreateDate);
        return list(qw);
    }

    /**
     * 添加患者收货地址
     *
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addPatientDeliveryAddr(PatientDeliveryAddrCreateReq req) {
        QueryWrapper<PatientMedCard> qw = new QueryWrapper<>();
        qw.lambda().eq(PatientMedCard::getMedId, req.getMedId())
                .eq(PatientMedCard::getStatus, UserConstants.MedCardStatus.NORMAL);
        PatientMedCard card = patientMedCardService.getOne(qw);
        if (card == null) {
            throw new ZekeException(400, "就诊卡号不存在");
        }
        //todo 校验是否添加了相同地址
        req.setId(null);
        checkAddr(req);
        UpdateWrapper<PatientDeliveryAddr> uw = new UpdateWrapper<>();
        uw.lambda().set(PatientDeliveryAddr::getType, UserConstants.DeliveryTypeStatus.NO)
                .eq(PatientDeliveryAddr::getMedId, req.getMedId());
        update(uw);
        PatientDeliveryAddr addr = new PatientDeliveryAddr();
        BeanUtils.copyProperties(req, addr);
        addr.setCreateDate(LocalDateTime.now());
        addr.setStatus(UserConstants.DeliveryAddrStatus.NORMAL);
        addr.setType(UserConstants.DeliveryTypeStatus.YES);
        boolean flag = save(addr);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
        }
    }

    private void checkAddr(PatientDeliveryAddrCreateReq req) {
        QueryWrapper<PatientDeliveryAddr> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(PatientDeliveryAddr::getMedId, req.getMedId())
                .eq(PatientDeliveryAddr::getStatus, UserConstants.DeliveryAddrStatus.NORMAL);
        if (req.getId() != null) {
            queryWrapper.lambda().ne(PatientDeliveryAddr::getId, req.getId());
        }
        List<PatientDeliveryAddr> addrList = list(queryWrapper);
        if (CollectionUtils.isNotEmpty(addrList)) {
            String reqReceiver = req.getReceiver();
            String reqMobileNumber = req.getMobileNumber();
            String reqAddr = req.getAddr();
            String reqProvince = req.getProvince();
            String reqCity = req.getCity();
            String reqDistrict = req.getDistrict();
            String newStr = reqReceiver + reqMobileNumber + reqProvince + reqCity + reqDistrict + reqAddr;
            newStr = newStr.trim();
            for (PatientDeliveryAddr patientDeliveryAddr : addrList) {
                String receiver = patientDeliveryAddr.getReceiver();
                String mobileNumber = patientDeliveryAddr.getMobileNumber();
                String addr = patientDeliveryAddr.getAddr();
                String province = patientDeliveryAddr.getProvince();
                String city = patientDeliveryAddr.getCity();
                String district = patientDeliveryAddr.getDistrict();
                String oldStr = receiver + mobileNumber + province + city + district + addr;
                oldStr = oldStr.trim();
                if (newStr.equals(oldStr)) {
                    throw new ZekeException("请勿重复添加地址!");
                }
            }
        }
    }

    /**
     * 添加患者收货地址
     *
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addPatientDeliveryAddrH5(PatientDeliveryAddrH5CreateReq req) {
        if (StringUtils.isBlank(req.getMedId())) {
            throw new ZekeException(400, "就诊卡号不能为空");
        }
        QueryWrapper<PatientMedCard> qw = new QueryWrapper<>();
        qw.lambda().eq(PatientMedCard::getMedId, req.getMedId())
                .eq(PatientMedCard::getStatus, UserConstants.MedCardStatus.NORMAL);
        int count = patientMedCardService.count(qw);
        if (count == 0) {
            throw new ZekeException(400, "就诊卡号不存在");
        }
        //TODO 地址校验
        PatientDeliveryAddrCreateReq checkAddr = new PatientDeliveryAddrCreateReq();
        BeanUtils.copyProperties(req, checkAddr);
        checkAddr(checkAddr);
        UpdateWrapper<PatientDeliveryAddr> uw = new UpdateWrapper<>();
        uw.lambda().set(PatientDeliveryAddr::getType, UserConstants.DeliveryTypeStatus.NO)
                .eq(PatientDeliveryAddr::getMedId, req.getMedId());
        update(uw);
        PatientDeliveryAddr addr = new PatientDeliveryAddr();
        BeanUtils.copyProperties(req, addr);
        if (req.getId() == null) {
            addr.setCreateDate(LocalDateTime.now());
            addr.setStatus(UserConstants.DeliveryAddrStatus.NORMAL);
        }
        addr.setType(UserConstants.DeliveryTypeStatus.YES);
        boolean flag = saveOrUpdate(addr);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.ADD_ERROR);
        }
        //如何有编辑则同步用户信息 有收获地址表示更新,没有则新增不做操作 什么玩意......
        if (req.getId() != null) {
            updateUserAddress(addr);
        }

    }


    /**
     * 添加患者收货地址 h5 数组形式
     *
     * @param reqs
     */
    @Override
    public void addPatientDeliveryAddrsH5(List<PatientDeliveryAddrH5CreateReq> reqs) {
        List<Integer> ids = reqs.stream().map(PatientDeliveryAddrH5CreateReq::getId).filter(Objects::nonNull).collect(Collectors.toList());
        if (ids.size() > 0) {
            //获取患者需要删除的地址
            LambdaQueryWrapper<PatientDeliveryAddr> qw = new LambdaQueryWrapper<>();
            qw.eq(PatientDeliveryAddr::getMedId, reqs.get(0).getMedId())
                    .notIn(PatientDeliveryAddr::getId, ids)
                    .eq(PatientDeliveryAddr::getStatus, UserConstants.DeliveryAddrStatus.NORMAL);
            List<PatientDeliveryAddr> patientAddressList = this.list(qw);
            if (CollectionUtils.isNotEmpty(patientAddressList)) {
                List<Integer> removeIds = patientAddressList.stream().map(PatientDeliveryAddr::getId).collect(Collectors.toList());
                //删除用户相关联的数据
                removeAddress(removeIds);
                //删除就诊人地址
                UpdateWrapper<PatientDeliveryAddr> uw = new UpdateWrapper<>();
                uw.lambda().eq(PatientDeliveryAddr::getMedId, reqs.get(0).getMedId());
                uw.lambda().in(PatientDeliveryAddr::getId, removeIds);
                uw.lambda().set(PatientDeliveryAddr::getStatus, UserConstants.DeliveryAddrStatus.DEL);
                update(uw);
            }
        }
        for (PatientDeliveryAddrH5CreateReq req : reqs) {
            addPatientDeliveryAddrH5(req);
        }
    }

    public void removeAddress(List<Integer> removeIds) {
        if (CollectionUtils.isNotEmpty(removeIds)) {
            for (Integer removeAddressId : removeIds) {
                updateUserAddressStatus(removeAddressId, UserConstants.DeliveryAddrStatus.DEL);
            }
        }
    }

    /**
     * 修改患者收货地址
     *
     * @param req
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePatientDeliveryAddr(PatientDeliveryAddrUpdateReq req) {
        PatientDeliveryAddr addr = getById(req.getId());
        if (addr == null || addr.getStatus() == UserConstants.DeliveryAddrStatus.DEL) {
            throw new ZekeException(400, "就诊卡不存在");
        }
        //TODO 地址校验
        checkUpdateAddr(req, addr.getMedId());
        UpdateWrapper<PatientDeliveryAddr> uw = new UpdateWrapper<>();
        uw.lambda().set(PatientDeliveryAddr::getType, UserConstants.DeliveryTypeStatus.NO)
                .eq(PatientDeliveryAddr::getMedId, addr.getMedId());
        update(uw);
        BeanUtils.copyProperties(req, addr);
        addr.setType(UserConstants.DeliveryTypeStatus.YES);
        boolean flag = updateById(addr);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
        }
        //同步用户所有就诊人关联的地址
        updateUserAddress(addr);
    }

    @Override
    public void updateUserAddress(PatientDeliveryAddr addr) {
        LambdaQueryWrapper<PatientAddressLink> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PatientAddressLink::getMedAddressId, addr.getId())
                .eq(PatientAddressLink::getStatus, UserConstants.DeliveryAddrStatus.NORMAL);
        PatientAddressLink patientAddressLink = patientAddressLinkService.getOne(wrapper);
        if (patientAddressLink == null) {
            return;
        }
        //更新用户收获地址
        Integer userAddressId = patientAddressLink.getUserAddressId();
        PatientUserAddress patientUserAddress = patientUserAddressService.getById(userAddressId);
        if (patientUserAddress != null) {
            patientUserAddress.setAddr(addr.getAddr())
                    .setCity(addr.getCity())
                    .setProvince(addr.getProvince())
                    .setDistrict(addr.getDistrict())
                    .setMobileNumber(addr.getMobileNumber())
                    .setReceiver(addr.getReceiver());
            patientUserAddressService.updateById(patientUserAddress);
        }

        //更新就诊人地址
        wrapper.clear();
        wrapper.eq(PatientAddressLink::getStatus, UserConstants.DeliveryAddrStatus.NORMAL)
                .eq(PatientAddressLink::getUserAddressId, userAddressId)
                .ne(PatientAddressLink::getMedAddressId, addr.getId());
        List<PatientAddressLink> patientAddressLinks = patientAddressLinkService.list(wrapper);
        if (CollectionUtils.isNotEmpty(patientAddressLinks)) {
            List<Integer> medAddressIds = patientAddressLinks.stream().map(PatientAddressLink::getMedAddressId)
                    .collect(Collectors.toList());
            LambdaUpdateWrapper<PatientDeliveryAddr> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.in(PatientDeliveryAddr::getId, medAddressIds)
                    .set(PatientDeliveryAddr::getAddr, addr.getAddr())
                    .set(PatientDeliveryAddr::getDistrict, addr.getDistrict())
                    .set(PatientDeliveryAddr::getCity, addr.getCity())
                    .set(PatientDeliveryAddr::getProvince, addr.getProvince())
                    .set(PatientDeliveryAddr::getReceiver, addr.getReceiver())
                    .set(PatientDeliveryAddr::getMobileNumber, addr.getMobileNumber());
            this.update(updateWrapper);
        }
    }

    private void checkUpdateAddr(PatientDeliveryAddrUpdateReq createReq, String medId) {
        QueryWrapper<PatientDeliveryAddr> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(PatientDeliveryAddr::getMedId, medId)
                .eq(PatientDeliveryAddr::getStatus, UserConstants.DeliveryAddrStatus.NORMAL)
                .ne(PatientDeliveryAddr::getId, createReq.getId())
                .orderByDesc(PatientDeliveryAddr::getCreateDate);
        List<PatientDeliveryAddr> addrList = list(queryWrapper);
        if (CollectionUtils.isNotEmpty(addrList)) {
            String reqReceiver = createReq.getReceiver();
            String reqMobileNumber = createReq.getMobileNumber();
            String reqAddr = createReq.getAddr();
            String reqProvince = createReq.getProvince();
            String reqCity = createReq.getCity();
            String reqDistrict = createReq.getDistrict();
            String newStr = reqReceiver + reqMobileNumber + reqProvince + reqCity + reqDistrict + reqAddr;
            newStr = newStr.trim();
            for (PatientDeliveryAddr patientDeliveryAddr : addrList) {
                String receiver = patientDeliveryAddr.getReceiver();
                String mobileNumber = patientDeliveryAddr.getMobileNumber();
                String addr = patientDeliveryAddr.getAddr();
                String province = patientDeliveryAddr.getProvince();
                String city = patientDeliveryAddr.getCity();
                String district = patientDeliveryAddr.getDistrict();
                String oldStr = receiver + mobileNumber + province + city + district + addr;
                oldStr = oldStr.trim();
                if (newStr.equals(oldStr)) {
                    throw new ZekeException("请勿重复添加地址!");
                }
            }
        }
    }

    /**
     * 修改默认收货地址
     *
     * @param id
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTypeDeliveryAddr(Integer id) {
        PatientDeliveryAddr addr = getById(id);
        if (addr == null || addr.getStatus() == UserConstants.DeliveryAddrStatus.DEL) {
            throw new ZekeException(400, "就诊卡不存在");
        }
        UpdateWrapper<PatientDeliveryAddr> uw = new UpdateWrapper<>();
        uw.lambda().set(PatientDeliveryAddr::getType, UserConstants.DeliveryTypeStatus.NO)
                .eq(PatientDeliveryAddr::getMedId, addr.getMedId());
        update(uw);
        addr.setType(UserConstants.DeliveryTypeStatus.YES);
        boolean flag = updateById(addr);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
        }
    }

    /**
     * 修改收货地址状态
     *
     * @param id
     * @param status
     */
    @Override
    public void updateDeliveryAddrStatus(Integer id, Integer status) {
        PatientDeliveryAddr addr = getById(id);
        if (addr == null || addr.getStatus() == UserConstants.DeliveryAddrStatus.DEL) {
            throw new ZekeException(400, "就诊卡不存在");
        }
        if (addr.getStatus().equals(status)) {
            return;
        }
        if (!ObjectUtils.refrect(UserConstants.DeliveryAddrStatus.class, status)) {
            throw new ZekeException(ZkExceptionEnum.STATUS_DATA_ERROR);
        }
        addr.setStatus(status);
        boolean flag = updateById(addr);
        if (!flag) {
            throw new ZekeException(ZkExceptionEnum.UPDATE_ERROR);
        }
        //同步用户所有就诊人地址状态
        updateUserAddressStatus(id, status);
    }

    @Override
    public void updateUserAddressStatus(Integer id, Integer status) {
        if (UserConstants.DeliveryAddrStatus.DEL != status) {
            return;
        }
        LambdaQueryWrapper<PatientAddressLink> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PatientAddressLink::getMedAddressId, id)
                .eq(PatientAddressLink::getStatus, UserConstants.DeliveryAddrStatus.NORMAL);
        PatientAddressLink patientAddressLink = patientAddressLinkService.getOne(wrapper);
        if (patientAddressLink == null) {
            return;
        }
        Integer userAddressId = patientAddressLink.getUserAddressId();
        PatientUserAddress patientUserAddress = patientUserAddressService.getById(userAddressId);
        if (patientUserAddress != null) {
            patientUserAddress.setStatus(UserConstants.DeliveryAddrStatus.DEL);
            patientUserAddressService.updateById(patientUserAddress);
        }
        wrapper.clear();
        wrapper.eq(PatientAddressLink::getStatus, UserConstants.DeliveryAddrStatus.NORMAL)
                .eq(PatientAddressLink::getUserAddressId, userAddressId);
        List<PatientAddressLink> patientAddressLinks = patientAddressLinkService.list(wrapper);
        if (CollectionUtils.isNotEmpty(patientAddressLinks)) {
            List<Integer> linkIds = patientAddressLinks.stream().map(PatientAddressLink::getId).collect(Collectors.toList());
            List<Integer> medAddressIds = patientAddressLinks.stream().map(PatientAddressLink::getMedAddressId).collect(Collectors.toList());
            LambdaUpdateWrapper<PatientAddressLink> updateWrapper = new LambdaUpdateWrapper<>();
            updateWrapper.in(PatientAddressLink::getId, linkIds)
                    .set(PatientAddressLink::getStatus, UserConstants.DeliveryAddrStatus.DEL);
            patientAddressLinkService.update(updateWrapper);
            LambdaUpdateWrapper<PatientDeliveryAddr> updateWrapper2 = new LambdaUpdateWrapper<>();
            updateWrapper2.in(PatientDeliveryAddr::getId, medAddressIds)
                    .set(PatientDeliveryAddr::getStatus, UserConstants.DeliveryAddrStatus.DEL);
            this.update(updateWrapper2);
        }
    }

    /**
     * 添加临时地址
     *
     * @param body
     */
    @Override
    public void addDeliveryAddrTemp(String body) {
        JSONObject jObj = JSON.parseObject(body);
        if (jObj.getInteger("docId") == null) {
            throw new ZekeException(400, "医生id不能为空");
        }
        redisTemplate.opsForValue().set(jObj.getInteger("docId") + "-addr-temp", body, 1L, TimeUnit.DAYS);
    }

    /**
     * 获取添加临时地址数据
     *
     * @param docId
     * @return
     */
    @Override
    public Map<String, Object> getDeliveryAddrTemp(Integer docId) {
        String body = redisTemplate.opsForValue().get(docId + "-addr-temp");
        return JSON.parseObject(body, HashMap.class);
    }
}
